package com.ma.implatform.service.impl;

import cn.hutool.core.date.DateUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.ma.common.contant.IMConstant;
import com.ma.common.enums.IMTerminalType;
import com.ma.common.model.IMPrivateMessage;
import com.ma.common.model.IMUserInfo;
import com.ma.common.utils.ThreadPoolExecutorFactory;
import com.ma.imclient.IMClient;
import com.ma.implatform.dto.PrivateMessageDTO;
import com.ma.implatform.entity.PrivateMessage;
import com.ma.implatform.enums.MessageStatus;
import com.ma.implatform.enums.MessageType;
import com.ma.implatform.exception.GlobalException;
import com.ma.implatform.mapper.PrivateMessageMapper;
import com.ma.implatform.service.FriendService;
import com.ma.implatform.service.PrivateMessageService;
import com.ma.implatform.session.SessionContext;
import com.ma.implatform.session.UserSession;
import com.ma.implatform.utils.BeanUtils;
import com.ma.implatform.vo.PrivateMessageVO;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.time.DateUtils;
import org.hibernate.validator.internal.engine.messageinterpolation.parser.MessageState;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.Date;
import java.util.List;
import java.util.Objects;
import java.util.concurrent.ScheduledThreadPoolExecutor;
import java.util.stream.Collectors;

@Slf4j
@Service
@RequiredArgsConstructor
public class PrivateMessageServiceImpl extends ServiceImpl<PrivateMessageMapper, PrivateMessage>   implements PrivateMessageService {
    private final FriendService friendService;
    private final IMClient imClient;
    private static final ScheduledThreadPoolExecutor EXECUTOR = ThreadPoolExecutorFactory.getThreadPoolExecutor();
    @Override
    public PrivateMessageVO sendMessage(PrivateMessageDTO dto) {
        UserSession session = SessionContext.getSession();
        Boolean isFriend = friendService.isFriend(session.getUserId(), dto.getRecvId());
        if (Boolean.FALSE.equals(isFriend)){
            throw new GlobalException("您已不是对方好友，无法发送消息");
        }
        //保存消息
        PrivateMessage msg = BeanUtils.copyProperties(dto, PrivateMessage.class);
        msg.setSendId(session.getUserId());
        msg.setStatus(MessageStatus.PENDING.code());
        msg.setSendTime(new Date());
        //TODO 过滤内容中的敏感词
        this.save(msg);
        //推送消息
        PrivateMessageVO msgInfo = BeanUtils.copyProperties(msg, PrivateMessageVO.class);
        IMPrivateMessage<PrivateMessageVO> sendMessage = new IMPrivateMessage<>();
        sendMessage.setRecvId(msgInfo.getRecvId());
        sendMessage.setSendToSelf(true);
        sendMessage.setData(msgInfo);
        sendMessage.setSendResult(true);
        imClient.sendPrivateMessage(sendMessage);
        log.info("发送私聊消息，发送id：{}，接受id:{},内容：{}",session.getUserId(),dto.getRecvId(),dto.getContent());
        return msgInfo;
    }

    @Override
    public PrivateMessageVO recallMessage(Long id) {
        UserSession session = SessionContext.getSession();
        PrivateMessage msg = this.getById(id);
        if (Objects.isNull(msg)){
            throw new GlobalException("消息不存在");
        }
        if (!msg.getSendId().equals(session.getUserId())){
            throw new GlobalException("这条消息不是由您发送，无法撤回");
        }
        if (System.currentTimeMillis()-msg.getSendTime().getTime()> IMConstant.ALLOW_RECALL_SECOND){
            throw new GlobalException("消息已发送超过5分钟，无法撤回");
        }
        //修改消息状态
        msg.setStatus(MessageStatus.RECALL.code());
        this.updateById(msg);
        //生成一条撤回消息
        PrivateMessage recallMsg = new PrivateMessage();
        recallMsg.setSendId(session.getUserId());
        recallMsg.setStatus(MessageStatus.PENDING.code());
        recallMsg.setSendTime(new Date());
        recallMsg.setType(MessageType.RECALL.code());
        recallMsg.setContent(id.toString());
        recallMsg.setRecvId(msg.getRecvId());
        this.save(recallMsg);
        //推送消息
        PrivateMessageVO msgInfo = BeanUtils.copyProperties(recallMsg, PrivateMessageVO.class);
        IMPrivateMessage<PrivateMessageVO> sendMessage = new IMPrivateMessage<>();
        sendMessage.setSend(new IMUserInfo(session.getUserId() ,session.getTerminal()));
        sendMessage.setData(msgInfo);
        imClient.sendPrivateMessage(sendMessage);
        log.info("撤回私聊消息，发送id：{}，接受id：{}，内容：{}",msg.getSendId(),msg.getRecvId(),msg.getContent());
        return msgInfo;
    }

    @Override
    public List<PrivateMessageVO> findHistoryMessage(Long friendId, Long page, Long size) {
        page = page >0 ? page:1;
        size = size >0 ? size :10;
        Long userId = SessionContext.getSession().getUserId();
        long stIdx = (page-1) * size;
        QueryWrapper<PrivateMessage> queryWrapper = new QueryWrapper<>();
        queryWrapper.lambda().and(
            w -> w.and(wp -> wp.eq(PrivateMessage::getSendId,userId).eq(PrivateMessage::getRecvId,friendId))
                    .or(wp->wp.eq(PrivateMessage::getRecvId,userId).eq(PrivateMessage::getSendId,friendId))
        ).ne(PrivateMessage::getStatus,MessageStatus.RECALL.code()).orderByDesc(PrivateMessage::getId)
        .last("limit "+stIdx+ ","+size);

        List<PrivateMessage> messages = this.list(queryWrapper);
        List<PrivateMessageVO> messageList = messages.stream().map(m -> BeanUtils.copyProperties(m, PrivateMessageVO.class))
                .collect(Collectors.toList());
        log.info("拉取聊天记录，用户id：{}，好友id：{}，数量：{}",userId,friendId,messageList.size());
        return messageList;
    }

    @Override
    public void pullOfflineMessage(Long minId) {
        UserSession session = SessionContext.getSession();
        //获取当前用户的消息
        LambdaQueryWrapper<PrivateMessage> wrapper = Wrappers.lambdaQuery();
        //只能拉取最近1个月的消息
        Date minDate = DateUtils.addMonths(new Date(), -1);
        wrapper.gt(PrivateMessage::getId,minId);
        wrapper.ge(PrivateMessage::getSendTime,minDate);
        wrapper.and(wp -> wp.eq(PrivateMessage::getSendId,session.getUserId()).or().eq(PrivateMessage::getRecvId,session.getUserId()));
        List<PrivateMessage> messages = this.list(wrapper);
        //异步推送消息
        EXECUTOR.execute(()->{
            //开启加载中标志
            this.sendLoadingMessage(true,session);
            for (PrivateMessage m : messages) {
                // 推送过程中如果用户下线了，则不再推送
                if (!imClient.isOnline(session.getUserId(), IMTerminalType.fromCode(session.getTerminal()))){
                    log.info("用户已下线，停止推送离线私聊消息，用户ID：{}",session.getUserId());
                    return;
                }
                PrivateMessageVO vo = BeanUtils.copyProperties(m, PrivateMessageVO.class);
                IMPrivateMessage<PrivateMessageVO> sendMessage = new IMPrivateMessage<>();
                sendMessage.setSend(new IMUserInfo(m.getSendId(),IMTerminalType.WEB.code()));
                sendMessage.setRecvId(session.getUserId());
                sendMessage.setSendToSelf(false);
                sendMessage.setData(vo);
                sendMessage.setSendResult(true);
                imClient.sendPrivateMessage(sendMessage);
            }
            //关闭加载中标志
            this.sendLoadingMessage(false,session);
            log.info("拉取私聊消息，用户id：{}",session.getUserId(),messages.size());
        });
    }

    private void sendLoadingMessage(Boolean isLoadding, UserSession session) {
        PrivateMessageVO msgInfo = new PrivateMessageVO();
        msgInfo.setType(MessageType.LOADING.code());
        msgInfo.setContent(isLoadding.toString());
        IMPrivateMessage<PrivateMessageVO> sendMessage = new IMPrivateMessage<>();
        sendMessage.setSend(new IMUserInfo(session.getUserId(), session.getTerminal()));
        sendMessage.setRecvId(session.getUserId());
        sendMessage.setRecvTerminals(List.of(session.getTerminal()));
        sendMessage.setData(msgInfo);
        sendMessage.setSendToSelf(false);
        sendMessage.setSendResult(false);
        imClient.sendPrivateMessage(sendMessage);
    }

    @Override
    public List<PrivateMessageVO> loadOfflineMessage(Long minId) {
        UserSession session = SessionContext.getSession();
        // 获取当前用户的消息
        LambdaQueryWrapper<PrivateMessage> wrapper = Wrappers.lambdaQuery();
        // 只能拉取最近1个月的消息
        Date minDate = DateUtils.addMonths(new Date(), -1);
        wrapper.gt(PrivateMessage::getId, minId);
        wrapper.ge(PrivateMessage::getSendTime, minDate);
        wrapper.and(wp -> wp.eq(PrivateMessage::getSendId, session.getUserId()).or()
                .eq(PrivateMessage::getRecvId, session.getUserId()));
        wrapper.orderByAsc(PrivateMessage::getId);
        List<PrivateMessage> messages = this.list(wrapper);
        // 更新消息为送达状态
        List<Long> messageIds =
                messages.stream().filter(m -> m.getStatus().equals(MessageStatus.PENDING.code())).map(PrivateMessage::getId)
                        .collect(Collectors.toList());
        if (!messageIds.isEmpty()) {
            LambdaUpdateWrapper<PrivateMessage> updateWrapper = Wrappers.lambdaUpdate();
            updateWrapper.in(PrivateMessage::getId, messageIds);
            updateWrapper.set(PrivateMessage::getStatus, MessageStatus.DELIVERED.code());
            update(updateWrapper);
        }
        // 转换vo
        List<PrivateMessageVO> vos = messages.stream().map(m -> BeanUtils.copyProperties(m, PrivateMessageVO.class))
                .collect(Collectors.toList());
        log.info("拉取私聊消息，用户id:{},数量:{},minId:{}", session.getUserId(), messages.size(), minId);
        return vos;
    }

    @Transactional(rollbackFor = Exception.class)
    @Override
    public void readedMessage(Long friendId) {
        UserSession session = SessionContext.getSession();
        //推送消息给自己，清空会话列表上的已读数量
        PrivateMessageVO msgInfo = new PrivateMessageVO();
        msgInfo.setType(MessageType.READED.code());
        msgInfo.setSendId(session.getUserId());
        msgInfo.setRecvId(friendId);
        IMPrivateMessage<PrivateMessageVO> sendMessage = new IMPrivateMessage<>();
        sendMessage.setData(msgInfo);
        sendMessage.setSend(new IMUserInfo(session.getUserId(), session.getTerminal()));
        sendMessage.setSendResult(false);
        sendMessage.setSendToSelf(false);
        imClient.sendPrivateMessage(sendMessage);
        //推送回执消息给对方，更新已读状态
        msgInfo = new PrivateMessageVO();
        msgInfo.setType(MessageType.RECEIPT.code());
        msgInfo.setRecvId(friendId);
        sendMessage = new IMPrivateMessage<>();
        sendMessage.setSend(new IMUserInfo(session.getUserId(),session.getTerminal()));
        sendMessage.setRecvId(friendId);
        sendMessage.setSendToSelf(false);
        sendMessage.setSendResult(false);
        sendMessage.setData(msgInfo);
        imClient.sendPrivateMessage(sendMessage);
        //修改消息状态为已读
        LambdaUpdateWrapper<PrivateMessage> updateWrapper = Wrappers.lambdaUpdate();
        updateWrapper.eq(PrivateMessage::getSendId,friendId)
                .eq(PrivateMessage::getRecvId,session.getUserId())
                .eq(PrivateMessage::getStatus,MessageStatus.DELIVERED.code())
                .set(PrivateMessage::getStatus, MessageStatus.READED.code());
        this.update(updateWrapper);
        log.info("消息已读，接收方ID：{}，发送方id：{}",session.getUserId(),friendId);
    }

    @Override
    public Long getMaxReadedId(Long friendId) {
        UserSession session = SessionContext.getSession();
        LambdaQueryWrapper<PrivateMessage> wrapper = Wrappers.lambdaQuery();
        wrapper.eq(PrivateMessage::getSendId,session.getUserId())
                .eq(PrivateMessage::getRecvId,friendId)
                .eq(PrivateMessage::getStatus,MessageStatus.READED.code())
                .orderByDesc(PrivateMessage::getId)
                .select(PrivateMessage::getId).last("limit 1");
        PrivateMessage message = this.getOne(wrapper);
        if (Objects.isNull(message)){
            return -1L;
        }
        return message.getId();
    }
}
